/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package grex;

import grex.fitnessfunctions.IFitnessFunction;
import grex.fitnessfunctions.Classification.MultipleSolutionsFitness;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author RIK
 */
public class ResultSaverTest implements EvolutionListener {

    int TRAIN = 0, TEST = 1, WIN = 0, TIE = 1, LOSS = 2, J48_ACC = 0, AVG_ACC = 1, TYP_ACC = 2, GLB_LIKE = 3, GRP_LIKE = 4, IFS = 5, GRPS = 6;
    ArrayList<ArrayList<GP>> foldWinners = new ArrayList<ArrayList<GP>>();
    ArrayList<GP> j48Trees = new ArrayList<GP>();
    ArrayList<double[][]> wtlJ48Results = new ArrayList<double[][]>();
    ArrayList<double[][]> wtlAvgResults = new ArrayList<double[][]>();
    ArrayList<double[][]> accResults = new ArrayList<double[][]>();

    public void evolutionStarted(EvolutionProcess source) {
    }

    public void generationTerminated(EvolutionProcess source) {
    }

    public void batchTerminated(EvolutionProcess source) {
    }

    public void evolutionTerminated(EvolutionProcess source) {
        GP bgp = source.getBestGP();
        GP j48;
        Options options = bgp.getOptions();
        if (!options.getFITNESS_FUNCTION().equals("MultipleSolutionsFitness")) {
            return;
        }
        ArrayList<GP> winners;
        if (options.getWinners() != null) {
            winners = new ArrayList<GP>(options.getWinners());
            foldWinners.add(winners);
            j48 = ((MultipleSolutionsFitness) bgp.getFitnessFunction()).getJ48GP();
            j48Trees.add(j48);
        } else {
            return;
        }


        try {
            //   for (int f = 0; f < foldWinners.size(); f++) {
            double[][] wtlGrpResult = new double[2][3]; //SPara TRain acc Test acc för j48 mm
            double[][] wtlJ48Result = new double[2][3];
            double[][] accResult = new double[2][7];
            int nrGroups = 0;
            if (foldWinners.size() == 0) {//if we can't find anything we still have the original tree!
                ArrayList<GP> gps = new ArrayList<GP>();
                gps.add(j48);
                foldWinners.add(gps);
            }
            TreeComparator tc = new TreeComparator(winners);
            ArrayList<ArrayList<GP>> splitGroups = tc.getTreeGroups();
            for (ArrayList<GP> group : splitGroups) {
                if (group.size() > 2) {
                    nrGroups++;
                    GP typical = tc.getTestTypicalPredictionTree(group);
                    tc.winTieLoss(typical, j48, wtlJ48Result);
                    tc.winTieLoss(typical, group, wtlGrpResult);
                    accResult[TRAIN][TYP_ACC] += typical.getTrainAccError();
                    accResult[TEST][TYP_ACC] += typical.getTestAccError();
                    accResult[TRAIN][GRP_LIKE] += tc.groupAvgEquality(typical, group);
                    accResult[TEST][GRP_LIKE] += tc.groupAvgEquality(typical, group);
                    accResult[TRAIN][GLB_LIKE] += tc.groupAvgEquality(typical, winners);
                    accResult[TEST][GLB_LIKE] += tc.groupAvgEquality(typical, winners);
                    accResult[TEST][GRPS]++;
                    accResult[TRAIN][GRPS]++;
                }
            }
            if (nrGroups > 0) {
                accResult[TRAIN][TYP_ACC] = accResult[TRAIN][TYP_ACC] / nrGroups;
                accResult[TEST][TYP_ACC] = accResult[TEST][TYP_ACC] / nrGroups;
                accResult[TEST][GRP_LIKE] = accResult[TEST][GRP_LIKE] / nrGroups;
                accResult[TRAIN][GRP_LIKE] = accResult[TRAIN][GRP_LIKE] / nrGroups;
                accResult[TEST][GLB_LIKE] = accResult[TEST][GLB_LIKE] / nrGroups;
                accResult[TRAIN][GLB_LIKE] = accResult[TRAIN][GLB_LIKE] / nrGroups;
            } else {//There are no groups bigger than, do the same calculation (like the foreach loop) for all trees
                GP typical = tc.getTestTypicalPredictionTree(winners);
                tc.winTieLoss(typical, j48, wtlJ48Result);
                tc.winTieLoss(typical, winners, wtlGrpResult);
                accResult[TRAIN][TYP_ACC] += typical.getTrainAccError();
                accResult[TEST][TYP_ACC] += typical.getTestAccError();
                accResult[TRAIN][GRP_LIKE] += tc.groupAvgEquality(typical, winners);
                accResult[TEST][GRP_LIKE] += tc.groupAvgEquality(typical, winners);
                accResult[TRAIN][GLB_LIKE] += tc.groupAvgEquality(typical, winners);
                accResult[TEST][GLB_LIKE] += tc.groupAvgEquality(typical, winners);
            }

            accResult[TEST][IFS] += j48.calcIFs();
            accResult[TRAIN][IFS] += accResult[TEST][IFS];

            accResult[TRAIN][J48_ACC] = j48.getTrainAccError();
            accResult[TEST][J48_ACC] = j48.getTestAccError();
            accResult[TRAIN][AVG_ACC] = tc.groupAvgTrainACC(winners);
            accResult[TEST][AVG_ACC] = tc.groupAvgTestACC(winners);


            wtlJ48Results.add(wtlJ48Result);
            wtlAvgResults.add(wtlGrpResult);
            accResults.add(accResult);
            /*System.out.print("fold" + foldWinners.size() + " ");
            System.out.print("wins: " + wtlJ48Result[TEST][WIN] + " ");
            System.out.print("ties: " + wtlJ48Result[TEST][TIE] + " ");
            System.out.println("loss: " + wtlJ48Result[TEST][LOSS]);
            //*/
            //     }
        } catch (Exception e) {
            e.printStackTrace();
        }





        /*  try {
        PrintWriter p = new PrintWriter(new FileWriter(options.getDATA_FILE() + "_" + options.getTestFOLD() + "_" + System.currentTimeMillis() + ".txt"));

        System.out.println("Winners: " + winners.size());
        if (winners == null || winners.size() == 0) {
        return;
        }
        MultipleSolutionsFitness msf = (MultipleSolutionsFitness) winners.get(0).getFitnessFunction();

        for (GP gp : winners) {
        try {
        msf.recalculateFitness(gp);
        } catch (Exception ex) {
        ex.printStackTrace();
        }
        }

        TreeComparator tc = new TreeComparator(winners, msf.getJ48GP());
        String s = "" + options.getWinners().size() + " Alternative trees were found with "+tc.getUniquePartitions().size() + " unique predictinos\n";
        s += tc.gpVsGroupStructure() + "\n\n";
        //    s += tc.gpVsGroupPrediction() + "\n\n";
        s += tc.gpVsGroupStructurePrediction();
        s += "\nStructures Groups--------------------------------------------------------------------------";
        s += tc.toString();
        System.out.println(s);
        p.println(s);
        p.println(options);
        p.close();
        msf.reset();
        } catch (IOException ex) {
        Logger.getLogger(ResultSaver.class.getName()).log(Level.SEVERE, null, ex);
        }//*/
    }

    public void evolutionProcessTerminated(EvolutionProcess source) {
        NumberFormat nf = NumberFormat.getInstance();
        nf.setMaximumFractionDigits(3);
        double[][] wtlJ48TotScore = new double[2][3];
        double[][] wtlGrpTotScore = new double[2][3];
        double[][] avgACC = new double[2][7];
        double totSolutions = 0;
        double totGroups = 0;
        double nrOfBigGroups = 0;
        String s = "Typical tree selected based on TEST ensemble\n";
        s += "Fold:" + "\tSltns:" + "\tGrps>3: " + "\tIFS:" + "\tGLB_LIKE:"
                + "\tGRP_LIKE:" + "\t[W/Y/L]vsJ48:" + "\t[W/Y/L]vsGrp:"
                + "\tJ48_ACC:" + "\tAVG_ACC:" + "\tTYP_ACC:\n";
        for (int f = 0; f < wtlJ48Results.size(); f++) {
            double groups = accResults.get(f)[TEST][GRPS];//sumArray(wtlJ48Results.get(f)[TEST]);
            if (groups > 0) {
                nrOfBigGroups++;
            }
            normalizeArray(wtlJ48Results.get(f));
            normalizeArray(wtlAvgResults.get(f));
            incArray(wtlJ48TotScore, wtlJ48Results.get(f));
            incArray(wtlGrpTotScore, wtlAvgResults.get(f));
            incArray(avgACC, accResults.get(f));
            totSolutions += foldWinners.get(f).size();
            totGroups += groups;
            s += "F" + f + "\t" + nf.format(foldWinners.get(f).size()) + "\t" + groups + "\t";
            s += nf.format(accResults.get(f)[TEST][IFS]) + "\t";
            s += nf.format(accResults.get(f)[TEST][GLB_LIKE]) + "\t";
            s += nf.format(accResults.get(f)[TEST][GRP_LIKE]) + "\t";
            s += "[" + nf.format(wtlJ48Results.get(f)[TEST][WIN]) + "/"
                    + nf.format(wtlJ48Results.get(f)[TEST][TIE]) + "/"
                    + nf.format(wtlJ48Results.get(f)[TEST][LOSS]) + "]\t";
            s += "[" + nf.format(wtlAvgResults.get(f)[TEST][WIN]) + "/"
                    + nf.format(wtlAvgResults.get(f)[TEST][TIE]) + "/"
                    + nf.format(wtlAvgResults.get(f)[TEST][LOSS]) + "]\t";

            s += nf.format(accResults.get(f)[TEST][J48_ACC]) + "\t";
            s += nf.format(accResults.get(f)[TEST][AVG_ACC]) + "\t";
            s += nf.format(accResults.get(f)[TEST][TYP_ACC]) + "\n";
        }
        normalizeArray(wtlJ48TotScore);
        normalizeArray(wtlGrpTotScore);
        s += "AVG\t" + nf.format(totSolutions / wtlJ48Results.size()) + "\t" + nf.format(totGroups / wtlJ48Results.size()) + "\t";
        s += nf.format(avgACC[TEST][IFS] / wtlJ48Results.size()) + "\t";
        s += nf.format(avgACC[TEST][GLB_LIKE] / nrOfBigGroups) + "\t";
        s += nf.format(avgACC[TEST][GRP_LIKE] / nrOfBigGroups) + "\t";

        s += "[" + nf.format(wtlJ48TotScore[TEST][WIN]) + "/"
                + nf.format(wtlJ48TotScore[TEST][TIE]) + "/"
                + nf.format(wtlJ48TotScore[TEST][LOSS]) + "]\t";
        s += "[" + nf.format(wtlGrpTotScore[TEST][WIN]) + "/"
                + nf.format(wtlGrpTotScore[TEST][TIE]) + "/"
                + nf.format(wtlGrpTotScore[TEST][LOSS]) + "]\t";

        s += nf.format(avgACC[TEST][J48_ACC] / accResults.size()) + "\t";
        s += nf.format(avgACC[TEST][AVG_ACC] / accResults.size()) + "\t";
        s += nf.format(avgACC[TEST][TYP_ACC] /  accResults.size());
        s += "\n";
        try {
            PrintWriter p = new PrintWriter(new FileWriter(source.getOptions().getDATA_FILE() + "_TEST_" + source.getOptions().getPARAMETER_2() +"IFs_"+ System.currentTimeMillis() + ".txt"));
            System.out.println(s);
            p.write(s);
            p.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    public void incArray(double[][] array, double[][] inc) {
        if (array == null || inc == null || array.length < inc.length || array[0].length < inc[0].length) {
            return;
        }
        for (int r = 0; r < inc.length; r++) {
            for (int c = 0; c < inc[0].length; c++) {
                array[r][c] += inc[r][c];
            }
        }
    }

    public void normalizeArray(double[][] array) {
        for (int r = 0; r < array.length; r++) {
            double sum = sumArray(array[r]);
            if (sum == 0) {
                continue;
            }
            for (int c = 0; c < array[r].length; c++) {
                array[r][c] = array[r][c] / sum;
            }
        }
    }

    public double sumArray(double[] array) {
        double sum = 0;
        for (int i = 0; i < array.length; i++) {
            sum += array[i];
        }
        return sum;
    }
}
